package com.magicstone.common.pagination.service;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateFactory;

public class Paginator {
	private final static Log logger = LogFactory.getLog(Paginator.class);
	
	public static Object process(Invocation invocation) throws Throwable, IllegalAccessException{
		logger.debug("Starting analysis sql...");
		StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
		BoundSql boundSql = statementHandler.getBoundSql();
		//MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY);
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
		RowBounds rowBounds = (RowBounds)metaStatementHandler.getValue("delegate.rowBounds");
		if(rowBounds == null || rowBounds == RowBounds.DEFAULT){
			logger.debug("No need to paginate!");
			return invocation.proceed();
		}
		Configuration configuration = (Configuration)metaStatementHandler.getValue("delegate.configuration");
		Dialect.Type databaseType  = null;
		try{
			databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
			logger.debug("Database type:" + databaseType);
		} catch(Exception e){
			//ignore
		}
		Dialect dialect = null;
		if (databaseType != null) {
			switch (databaseType) {
			case MYSQL:
				dialect = new MySql5Dialect();
				logger.debug("MySQL pagination request!");
				break;
			case ORACLE:
				dialect = new OracleDialect();
				logger.debug("ORACLE pagination request!");
				break;
			case SQLSERVER2005:
				dialect = new SQLServer2005Dialect();
				logger.debug("SQLServer2005 pagination request!");
				break;
			case SQLSERVER:
				dialect = new SQLServerDialect();
				logger.debug("SQLServer pagination request!");
				break;
			default:
				logger.debug("Unsupported database pagination request!");
				break;
			}
		}
		String originalSql = (String)metaStatementHandler.getValue("delegate.boundSql.sql");
		if(null != dialect){
			metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()));
			metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET );
			metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT );
		}else{
			//metaStatementHandler.setValue("delegate.boundSql.sql", originalSql );
			logger.debug("Mybatis raw pagination");
			return invocation.proceed();
		}
		logger.debug("Paginating SQL : " + boundSql.getSql());
		return invocation.proceed();
	}
}
